#include "openplant.h"
#include "stringinfo.h"
#include "nodes/op_list.h"
#include "nodes/parsenodes.h"

/**
 *  global variables
 *
 */
const char *debug_query_string;  /* client-supplied query string */

/* Note: whereToSendOutput is initialized for the bootstrap/standalone case */
/* CommandDest whereToSendOutput = DestDebug; */

/**
 * people who want to use EOF should #define DONTUSENEWLINE in
 * tcop/tcopdebug.h 
 */
#ifndef TCOP_DONTUSENEWLINE
static int UseNewLine = 1;  /* use newline query delimiters (the default) */
#else
static int UseNewLine = 0;  /* use EOF as query delimiters */
#endif

static bool EchoQuery = false;  /* default don't echo */

/**
 * decls for routines only unsed in this file
 * _line_0170
 */
static int InteractiveBackend(StringInfo inBuf);
static int interactive_getc(void);
static int ReadCommand(StringInfo inBuf);

/**
 * InteractiveBackend() is called for user interactive connections
 * 
 * the string entered by the user is placed in its parameter inBuf,
 * and we act like a Q message was received.
 * 
 * EOF is returned if end-of-file input is seen, time to shut down.
 * _line_0203
 */
static int InteractiveBackend(StringInfo inBuf)
{
  int c;  /* character read from getc() */
  bool end = false;  /* end-of-input flag */
  bool backslashSeen = false;  /* have seen a \? */

  /* display a prompt and obtain input from the user */
  printf("backed> ");
  fflush(stdout);

  resetStringInfo(inBuf);

  if (UseNewLine) {
    /* if we are using \n as delimiter, then read characters untile the \n */
    while ((c = interactive_getc()) != EOF) {
      if (c == '\n') {
        if (backslashSeen) {
          /* discard backslash from inBuf */
          inBuf->data[--inBuf->len] = '\0';
          backslashSeen = false;
          continue;
        } else {
          /* keep the newline character */
          appendStringInfoChar(inBuf, '\n');
          break;
        }
      } else if (c == '\\') {
        backslashSeen = true;
      } else {
        backslashSeen = false;
      }
      appendStringInfoChar(inBuf, (char) c);        
    }
    if (c == EOF) {
      end = true;
    }
  } else {
    /* otherwise read characters until EOF. */
    while ((c = interactive_getc()) != EOF) {
      appendStringInfoChar(inBuf, (char) c);
    }
    
    /* No input before EOF signal means time to quit */
    if (inBuf->len == 0) {
      end = true;
    }
  }
  
  if (end) 
    return EOF;

  /* otherwise we have a user query to process it. */
  /* add '\0' to make it look the same as message case. */
  appendStringInfoChar(inBuf, (char) '\0');

  /* if the query echo flag was given, print the query.. */

  if (EchoQuery) {
    printf("statement: %s\n", inBuf->data);
  }
  fflush(stdout);

  return 'Q';
}

/** 
 * interactive_getc -- collect one character from stdin
 * _line_0293
 */
static int interactive_getc(void)
{
  int c;
  c = getc(stdin);
  return c;
}

/**
 * ReadCommand - 
 *   reads a command from either the frontend or 
 *   standard input, places it in inBuf, and returns
 *   the message type code (first byte of the message).
 *   EOF is returned if end of file.
 * _line_0436
 */
static int ReadCommand(StringInfo inBuf)
{
  int result;
  if (0) /* we should add other input source */
    ;
  else
    result = InteractiveBackend(inBuf);

  return result;
}

/**
 * Given a raw parsetree (gram.y output), and optionally information about
 * types of parameter symbols ($n), perform parse analysis and rule rewriting.
 *
 * A list of Query nodes is returned, since either the analyzer or the
 * rewriter might expand one query to several.
 *
 * NOTE: for reasons mentioned above, this must be separate from raw parsing.
 * _line_0591
 */
List *op_analyze(Node *parsetree, const char *query_string, Oid *paramTypes, int numParams)
{

	Query	   *query;
	List	   *querytree_list;
#if 0
	TRACE_POSTGRESQL_QUERY_REWRITE_START(query_string);

	/*
	 * (1) Perform parse analysis.
	 
	if (log_parser_stats)
		ResetUsage();*/

	query = parse_analyze(parsetree, query_string, paramTypes, numParams);

	/* if (log_parser_stats)
		ShowUsage("PARSE ANALYSIS STATISTICS"); */

	/*
	 * (2) Rewrite the queries, as necessary
	 */
	querytree_list = pg_rewrite_query(query);

	TRACE_POSTGRESQL_QUERY_REWRITE_DONE(query_string);
#endif

	return querytree_list;
}

/**
 * Do raw parsing (only).
 *
 * A list of parsetrees is returned, since there might be multiple
 * commands in the given string.
 *
 * NOTE: for interactive queries, it is important to keep this routine
 * separate from the analysis & rewrite stages.  Analysis and rewriting
 * cannot be done in an aborted transaction, since they require access to
 * database tables.  So, we rely on the raw parser to determine whether
 * we've seen a COMMIT or ABORT command; when we are in abort state, other
 * commands are not processed any further than the raw parse stage.
 */
List *op_parse_query(const char *query_string)
{
  List *raw_parsetree_list;

  raw_parsetree_list = raw_parser(query_string);

  return raw_parsetree_list;
}

/**
 * exec_simple_query
 *  execute a simple query.
 * _line_0772
 */
static void exec_simple_query(const char *query_string)
{
  List *parsetree_list;
  ListCell *parsetree_item;
  bool isTopLevel;
  
  parsetree_list = op_parse_query(query_string);

  /* we'll tell PortRun it's a top-level command there's exactly one
   * raw parsetree. If more than one, it's effectively a transaction
   * block and we want PreventTransactionChain to reject unsafe commands.
   */
  isTopLevel = (list_length(parsetree_list) == 1);

  /* run through the raw parsetree(s) and process each one. */
  foreach(parsetree_item, parsetree_list) {
    Node *parsetree = (Node *)lfirst(parsetree_item);
    const char *commandTag;
    List *querytree_list;
    /* Portal protal; */
    int16 format;

    /* */
    querytree_list = op_analyze(parsetree, query_string, NULL, 0);
  }  
}

/**
 * openPlantMain
 *   openPlant main loop -- all backends, interactive or otherwise start here
 * _line_2851 
 */
int openPlantMain()
{
  int firstchar;
  StringInfoData input_message;

  for (;;) {
    initStringInfo(&input_message);

    firstchar = ReadCommand(&input_message);

    switch(firstchar) {
    case 'Q':  /* simple query */
      {
        const char *query_string;
        query_string = &input_message.data[input_message.cursor];
        if (input_message.cursor != input_message.len) {
			printf("openPlantMain_line_213: invalid message format\n");
        }

        exec_simple_query(query_string);        
      }
      break;
    default:
      printf("invalid frontend message type %d", firstchar);
    }
  }
  Assert(false);  /* can't get here because the above loop never exits */

  return 1;  /* keep compiler quite */
}
